﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Principal;
using System.Xml.Linq;

using XCommunity.Frames;
using XCommunity.Users;

namespace XCommunity.Forums
{
  public class ForumsUserService : IUserService
  {

    public ForumsUserService( ForumsSystem forums )
    {
      ForumsSystem = forums;
    }


    protected ICommunityContext Context
    {
      get { return ForumsSystem.Context; }
    }

    protected ForumsSystem ForumsSystem
    {
      get;
      private set;
    }



    public void Init()
    {
      LoadAuthoritiesConfig( ForumsSystem.Configuration.Root.Element( ForumsSystem.XF + "users" ).Element( ForumsSystem.XF + "authorities" ) );
    }


    public ForumsUser GetUser()
    {
      return GetUser( Context.CurrentAccount );
    }

    protected ForumsUser GetUser( IIdentity account )
    {

      if ( account == null || !account.IsAuthenticated )
        return null;

      return new ForumsUser( account, new string[0] );
    }


    IPrincipal IUserService.GetUser()
    {
      return GetUser();
    }


    /// <summary>
    /// 获取当前登录用户的权限
    /// </summary>
    /// <param name="frameObject"></param>
    /// <returns></returns>
    public Permission GetUserPermissions()
    {
      return new UserPermission( GetUser(), this );
    }



    private IDictionary<string, IControlItem> _controlItems = new Dictionary<string, IControlItem>();

    /// <summary>
    /// 预定义的权限控制项
    /// </summary>
    public IDictionary<string, IControlItem> DefinedControlItems
    {
      get { return _controlItems; }
    }



    private Dictionary<string, IControlItem[]> _controlGroups = new Dictionary<string, IControlItem[]>();

    /// <summary>
    /// 预定义的权限控制项组
    /// </summary>
    public IDictionary<string, IControlItem[]> DefinedControlGroups
    {
      get { return _controlGroups; }
    }



    /// <summary>
    /// 加载授权配置
    /// </summary>
    /// <param name="configElement"></param>
    protected void LoadAuthoritiesConfig( XElement configuration )
    {
      foreach ( var element in configuration.Elements( ForumsSystem.XF + "control-item" ) )
      {
        string name;

        var controlItem = LoadControlItem( element, out name );

        DefinedControlItems.Add( name, controlItem );
      }


      foreach ( var element in configuration.Elements( ForumsSystem.XF + "control-group" ) )
      {
        string name;

        var group = LoadControlGroup( element, out name );

        _controlGroups.Add( name, group.ToArray() );
      }

    }



    /// <summary>
    /// 加载预定义的权限控制项组
    /// </summary>
    /// <param name="element">权限项组配置节</param>
    /// <param name="name">预定义的组名称</param>
    /// <returns></returns>
    public IControlItem[] LoadControlGroup( XElement configuration, out string name )
    {


      if ( configuration.Attribute( "ref" ) != null )
        throw new ConfigurationErrorException( "此处不允许使用 \"ref\" 属性", configuration );



      if ( configuration.Attribute( "name" ) == null )
        throw new ConfigurationErrorException( "缺少必需的属性 \"name\"", configuration );

      name = configuration.Attribute( "name" ).Value;

      return configuration.Elements( ForumsSystem.XF + "control-item" ).Select( item => LoadControlItem( item ) )
        .Union( configuration.Elements( ForumsSystem.XF + "control-group" ).SelectMany( item => LoadControlGroup( item ) ) ).ToArray();

    }


    /// <summary>
    /// 加载权限控制项组
    /// </summary>
    /// <param name="element">权限项组配置节</param>
    /// <returns></returns>
    public IControlItem[] LoadControlGroup( XElement configuration )
    {


      var refAttribute = configuration.Attribute( "ref" );

      if ( refAttribute != null )
      {

        if ( configuration.Attribute( "name" ) != null )
          throw new ConfigurationErrorException( "\"ref\" 属性不能与 \"name\" 属性同时存在", configuration );

        if ( configuration.HasElements )
          throw new ConfigurationErrorException( "\"ref\" 属性不能与任何子元素同时存在", configuration );

        IControlItem[] items;
        if ( !DefinedControlGroups.TryGetValue( refAttribute.Value, out items ) )
          throw new ConfigurationErrorException( "找不到权限项", refAttribute );

        return items;
      }
      else
      {
        if ( configuration.Attribute( "name" ) != null )
          throw new ConfigurationErrorException( "此处不允许使用 \"name\" 属性", configuration );

        return _LoadControlGroup( configuration );
      }

    }


    private IControlItem[] _LoadControlGroup( XElement configuration )
    {
      return
        configuration.Elements( ForumsSystem.XF + "control-item" )
        .Select( item => LoadControlItem( item ) )
        .Union
        (
          configuration.Elements( ForumsSystem.XF + "control-group" )
          .SelectMany( item => LoadControlGroup( item ) )
        ).ToArray();
    }


    /// <summary>
    /// 加载预定义的权限控制项
    /// </summary>
    /// <param name="element">权限项配置节</param>
    /// <param name="name">输出预定义的控制项名称</param>
    /// <returns></returns>
    protected IControlItem LoadControlItem( XElement configation, out string name )
    {

      if ( configation.Attribute( "ref" ) != null )
        throw new ConfigurationErrorException( "此处不允许使用 \"ref\" 属性", configation );


      if ( configation.Attribute( "name" ) == null )
        throw new ConfigurationErrorException( "缺少必需的属性 \"name\"", configation );

      name = configation.Attribute( "name" ).Value;


      return _LoadControlItem( configation );

    }


    /// <summary>
    /// 加载权限控制项
    /// </summary>
    /// <param name="element">权限项配置节</param>
    /// <returns></returns>
    protected IControlItem LoadControlItem( XElement configuration )
    {
      var refAttribute = configuration.Attribute( "ref" );

      if ( refAttribute != null )
      {

        if ( configuration.Attribute( "name" ) != null )
          throw new ConfigurationErrorException( "\"ref\" 属性不能与 \"name\" 属性同时存在", configuration );

        if ( configuration.HasElements )
          throw new ConfigurationErrorException( "\"ref\" 属性不能任何子元素同时存在", configuration );


        string name = refAttribute.Value;
        IControlItem item;

        if ( !DefinedControlItems.TryGetValue( name, out item ) )
          throw new ConfigurationErrorException( "找不到权限项", refAttribute );


        return item;

      }
      else
      {

        if ( configuration.Attribute( "name" ) != null )
          throw new ConfigurationErrorException( "此处不允许使用 \"name\" 属性", configuration );


        return _LoadControlItem( configuration );
      }
    }


    private IControlItem _LoadControlItem( XElement configuration )
    {

      var constraintElement = configuration.Element( ForumsSystem.XF + "constraint" );

      if ( constraintElement == null )
        throw new ConfigurationErrorException( "缺少必需的元素 \"constraint\"", configuration );

      var condition = LoadControlConstraint( constraintElement );



      var controlTypeElement = configuration.Element( ForumsSystem.XF + "control-type" );

      if ( controlTypeElement == null )
        throw new ConfigurationErrorException( "缺少必需的元素 \"control-type\"", configuration );

      var controlType = LoadEntryControlType( controlTypeElement );

      return new ControlItem( condition, controlType );
    }



    /// <summary>
    /// 加载权限控制级别
    /// </summary>
    /// <param name="element"></param>
    /// <returns></returns>
    protected ControlItemType LoadEntryControlType( XElement configuration )
    {
      return (ControlItemType) Enum.Parse( typeof( ControlItemType ), configuration.Value );
    }


    /// <summary>
    /// 从配置中加载权限项限制
    /// </summary>
    /// <param name="element"></param>
    /// <returns></returns>
    protected IControlConstraint LoadControlConstraint( XElement configuration )
    {

      return new ConstraintConfig( configuration );

    }

  }
}
